Skip to content

Support launching Multi-Release-Compiled Projects#753

Merged
stephan-herrmann merged 1 commit intoeclipse-jdt:masterfrom
laeubi:mr_launching
Sep 27, 2025
Merged

Support launching Multi-Release-Compiled Projects#753
stephan-herrmann merged 1 commit intoeclipse-jdt:masterfrom
laeubi:mr_launching

Conversation

@laeubi
Copy link
Copy Markdown
Contributor

@laeubi laeubi commented Aug 1, 2025

Currently when one launches a multi-release compiled project one always gets the type from the default project folder.

This now first checks what JRE is used to launch, then adds all valid folders in reverse version order to the classpath to emulate the behavior of loading a multi-release jar at runtime.

Fix #755

@stephan-herrmann can you review? This is basically applying what we did for the compile case but at runtime.

@stephan-herrmann
Copy link
Copy Markdown
Contributor

This looks promising.

Also here I would like to see a few junit tests. As we would need to assert the effect of a launched program, likely by way of its console output, perhaps org.eclipse.jdt.debug.tests.core.ConsoleTests.testConsoleOutputSynchronization() is the closest example to start from (but then to be placed into the package o.e.j.debug.tests.launching, I suppose).

FYI, while playing with this feature I created MRTest.zip including 3 launch configurations that produce different results.

Interestingly, for me Main 17.launch and Main 24.launch produced the same output. At a closer look this happened, because launching with EE JavaSE-17 still used the physical JDK-24 -- I had no JDK-17 registered in Eclipse.

To prevent surprised and unhappy users I could see two options:

  • use the configured EE rather than the physical JDK for selecting class folders (might break if the newer physical JDK no longer contains some API which was present in the specified version)
  • or, raise an error in a launch configuration if
    • the selected project has a multi-release configuration
    • and the selected EE does not have a perfect matching JRE configured in Eclipse

@laeubi
Copy link
Copy Markdown
Contributor Author

laeubi commented Aug 3, 2025

Interestingly, for me Main 17.launch and Main 24.launch produced the same output. At a closer look this happened, because launching with EE JavaSE-17 still used the physical JDK-24 -- I had no JDK-17 registered in Eclipse.

This is exactly how it is supposed to work. An execution environment is only an abstraction to select a physical VM but does not has any meaning on runtime, so the behavior is correct and wanted.

The exactly same would happen if the launch contains a already packaged MR-jar and there is nothing one can do to influence that behavior.

@stephan-herrmann
Copy link
Copy Markdown
Contributor

Interestingly, for me Main 17.launch and Main 24.launch produced the same output. At a closer look this happened, because launching with EE JavaSE-17 still used the physical JDK-24 -- I had no JDK-17 registered in Eclipse.

This is exactly how it is supposed to work. An execution environment is only an abstraction to select a physical VM but does not has any meaning on runtime, so the behavior is correct and wanted.

The exactly same would happen if the launch contains a already packaged MR-jar and there is nothing one can do to influence that behavior.

I read this as a vote for my second option

raise an error in a launch configuration if

  • the selected project has a multi-release configuration
  • and the selected EE does not have a perfect matching JRE configured in Eclipse

Motivation: when a user selects a specific EE for launching a multi-release program, they likely want to test how it works on that particular version (unless, perhaps, the EE matches the project's default release version). I won't insist on this being an error, but some alert should be given.

From your comment I could also guess that such a launch should preferably select a physical JRE rather than an EE?

I see that you are well aware of this issue, but we should also do our best for other users to learn these fine points with minimal pain :)

@laeubi
Copy link
Copy Markdown
Contributor Author

laeubi commented Aug 4, 2025

I read this as a vote for my second option

My vote would be to do nothing in this area (at least not at this stage). If I where to develop and test such feature I would expect that I always use a specific JVM and not an EE. But of course I would not say it is forbidden and can be used if configured correctly.

As when I run a program it shows me the actually used physical JVM I also don't see a high risk of confusion because one always can see what one gets.

Second, one might argue that such a warning is required for any MR jar on the classpath of any launch then because this could have the same surprising effect of course then as well. I'm just not sure such extra errors/warnings dialog pay of the efforts.

So given we have all these technical issues solved and then there is a high demand from users because they constantly fail to understand the issue, then it might be considered to add something, but then it has to be done on a broader scope, especially as since modular JVMs have become mainstream the concept of EE has become a bit obsolete anyways.

@laeubi
Copy link
Copy Markdown
Contributor Author

laeubi commented Aug 4, 2025

@stephan-herrmann I now added a test-case for this based on your example.

@stephan-herrmann
Copy link
Copy Markdown
Contributor

I read this as a vote for my second option

My vote would be to do nothing in this area (at least not at this stage). If I where to develop and test such feature I would expect that I always use a specific JVM and not an EE. But of course I would not say it is forbidden and can be used if configured correctly.

I moved this topic to #756 (as mentioned, I don't expect every user to be sharply aware of these fine points).

Copy link
Copy Markdown
Contributor

@stephan-herrmann stephan-herrmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test issue "Illegal Thread access" must be resolved.

I also dropped some lesser comments.

Testing this locally, I came across an "infrastructure problem": Initially, tests did not find all required JREs, and as a result it ran all variants using the same JRE (in my case: 24) and failed.

So this test introduces a requirement, that JREs 11, 17 and 21 are installed in a way that can be discovered by DetectVMInstallationsJob.

On my machines I installed tons of JDKs in a non-standard, user-owned location.

Only be inspecting the code in DetectVMInstallationsJob I found a way to make my installations known for the benefit of this test: link the location of JDK installs into ~/.sdkman/candidates/java.

I wonder if this will cause headaches for other jdt.debug developers?

At the very least this requirement should be documented in the new test.

@iloveeclipse @subyssurendran666 @SougandhS anyone care to comment?

@iloveeclipse
Copy link
Copy Markdown
Member

On my machines I installed tons of JDKs in a non-standard, user-owned location.

Only be inspecting the code in DetectVMInstallationsJob I found a way to make my installations known for the benefit of this test: link the location of JDK installs into ~/.sdkman/candidates/java.

I wonder if this will cause headaches for other jdt.debug developers?

At the very least this requirement should be documented in the new test.

@iloveeclipse @subyssurendran666 @SougandhS anyone care to comment?

DetectVMInstallationsJob.disabled property is set for model tests, so if we now rely on specific JVM's to be found, it has also to be set in debug tests like:

https://github.com/eclipse-jdt/eclipse.jdt.core/blob/0c310d0cd717992d06719934ed1c9d53daf13acc/org.eclipse.jdt.core.tests.model/pom.xml#L37

Or do you mean something else?

@stephan-herrmann
Copy link
Copy Markdown
Contributor

On my machines I installed tons of JDKs in a non-standard, user-owned location.
Only be inspecting the code in DetectVMInstallationsJob I found a way to make my installations known for the benefit of this test: link the location of JDK installs into ~/.sdkman/candidates/java.
I wonder if this will cause headaches for other jdt.debug developers?
At the very least this requirement should be documented in the new test.
@iloveeclipse @subyssurendran666 @SougandhS anyone care to comment?

DetectVMInstallationsJob.disabled property is set for model tests, so if we now rely on specific JVM's to be found, it has also to be set in debug tests like:

https://github.com/eclipse-jdt/eclipse.jdt.core/blob/0c310d0cd717992d06719934ed1c9d53daf13acc/org.eclipse.jdt.core.tests.model/pom.xml#L37

Or do you mean something else?

Here it is not about disabling DetectVMInstallationsJob, but about ensuring that it finds the exact version 11, 17 and 21 on developer machines where those exact versions may or may not exist in the standard location (linux: /usr/lib/jvm/*).

@iloveeclipse
Copy link
Copy Markdown
Member

Here it is not about disabling DetectVMInstallationsJob, but about ensuring that it finds the exact version 11, 17 and 21 on developer machines where those exact versions may or may not exist in the standard location (linux: /usr/lib/jvm/*).

Still not sure what you mean. Both mentioned locations are already on the search path. Do you mean, you would like to filter found JVM's additionally by some extra flag?

private Collection<File> computeCandidateVMs(StandardVMType standardType) {
// parent directories containing a collection of VM installations
Collection<File> rootDirectories = new HashSet<>();
if (Platform.OS.isWindows()) {
computeWindowsCandidates(rootDirectories);
} else {
rootDirectories.add(new File("/usr/lib/jvm")); //$NON-NLS-1$
}
rootDirectories.add(new File(System.getProperty("user.home"), ".sdkman/candidates/java")); //$NON-NLS-1$ //$NON-NLS-2$
rootDirectories.add(new File(miseDataDir(), "installs/java")); //$NON-NLS-1$

@stephan-herrmann
Copy link
Copy Markdown
Contributor

Here it is not about disabling DetectVMInstallationsJob, but about ensuring that it finds the exact version 11, 17 and 21 on developer machines where those exact versions may or may not exist in the standard location (linux: /usr/lib/jvm/*).

Still not sure what you mean.

Let my try again :)

When I first ran the new test on my local machine it failed. Failed not because any code is "wrong", but because DetectVMInstallationsJob makes assumptions that are not met on my local machine: it expects to find (most) VMs inside /usr/lib/jvm. But I cannot rely on my linux distro to provide all relevant JDK versions (in time), so /usr/lib/jvm is almost empty and I manually put all versions into a separate user-owned location. This works fine in the IDE, where I can manually add all I need via "Installed JREs", but that doesn't help this new test.

I have found a solution that works for me personally, but the question is: will current or future devs of jdt.debug run into trouble should they need to run tests locally?

Hence my proposal: let's add smth like the following as a comment in the new test:

Should you experience failures when running this test on your local machine, make sure that Eclipse finds proper JDKs version 11, 17 and 21 in one of these locations:

  • system default (linux: /usr/lib/jvm/)
  • ${HOME}/.sdkman/candidates/java/

And the question: is this enough to avoid unnecessary churn for jdt.debug devs?

@iloveeclipse
Copy link
Copy Markdown
Member

And the question: is this enough to avoid unnecessary churn for jdt.debug devs?

Why not add assertion with appropriate fail message at the beginning of the test?

@stephan-herrmann
Copy link
Copy Markdown
Contributor

And the question: is this enough to avoid unnecessary churn for jdt.debug devs?

Why not add assertion with appropriate fail message at the beginning of the test?

Sounds good to me. Can you add this, @laeubi ? And then the above hints (about /usr/lib/jvm, ${HOME}/.sdkman/candidates, possibly others?) should be given as a code comment near that assertion. At least I had never heard of .sdkman before looking into the impl of DetectVMInstallationsJob.

@stephan-herrmann
Copy link
Copy Markdown
Contributor

Why not add assertion with appropriate fail message at the beginning of the test?

Let me predict that such new assertion will fail on jenkins and thus explain the current failure:

X should be executed from Java 11 version: {X=17, Y=21, Z=17, Main=24+36-3646} expected:<11> but was:<17>

Perhaps jenkins no longer offers JDK 11?

@laeubi
Copy link
Copy Markdown
Contributor Author

laeubi commented Aug 6, 2025

Perhaps jenkins no longer offers JDK 11?

Autodiscovery of JVMs is disabled on CI servers (by intend) because it is running as a job it is often a possible source of flappy tests because it could trigger rebuilds or other effects.

Also for local tests I think it should not rely on this feature and I'll check how this can be done, in general it is of course a bit uncomfortable but I hope not every jdt-dev will need to run this test regularly so in case there is an issue one would need some more setup maybe.

In general I have opened this ticket here so we have better visibility of installed JDKs on the Jenkins:

https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/issues/6508

@laeubi
Copy link
Copy Markdown
Contributor Author

laeubi commented Aug 6, 2025

As a first step I have now added proper assertions to the test. In general I would not be happy to rely on simply enable auto-detect of JVMs for different reasons:

  1. From the code it seem not be able to discover the JVMs on the Jenkins as they are located in /opt/tools/java/openjdk/<java version>/<java install>, this might work better if we can have https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/issues/6508 resolved and Add support to detect prefixed JAVA_HOME_ env variables for installation #758 merged.
  2. Making tests to require an unknown modification of the system state seems wrong as it impose a risk of the test outcome depends on timing and in what order tests are run.

If everyone agree, I would therefore like to enhance the DetectVMInstallationsJob in a way that it allows to call a static method where I can pass in an own discovery strategy and then returning a list of added VMinstalls. That way the test can have a SystemProperty (defaulting to /opt/tools/java/openjdk/) where it searches for JVM installs and in the teardown of the test clean up these additional installs again. And a developer can then quite easy point to a directory where it has installed some JVMs to properly execute the test.

@laeubi
Copy link
Copy Markdown
Contributor Author

laeubi commented Aug 6, 2025

I now moved the search for JVMs from the JRE preference page to the DetectVMInstallationsJob as a static method to search JVMs by a base path and now using /opt/tools/java/openjdk/ as a default that can be overridden if required.
The test then searches there for suitable JVMs and dispose them at the end of the test again.

So if I anticipated everything correctly this should now work on the Jenkins (but requires adjustments on mac but this can only be seen in ibuids).

@laeubi
Copy link
Copy Markdown
Contributor Author

laeubi commented Aug 6, 2025

Jenkins is green now!

@stephan-herrmann
Copy link
Copy Markdown
Contributor

Close, but no cigar.

Running tests locally, I set MultiReleaseLaunchTests.rootDir to the local directory containing JDKs of "all" versions, but the test fails with

Was not launched with a proper Java installation {Java=24, X=17, Y=21, Z=17}

would be nice if the assertion also mentions the expected value Java=17

What happened? During setUp() we select one VM for each of JAVA_11, JAVA_17 and JAVA_21. These are the versions picked: 15, 19, 24. Interesting choice, but no error, right?

When launching a VM for JavaSE-17, JREContainerInitializer.resolveVM(IExecutionEnvironment) does not find a match that isStrictlyCompatible(). In this case JavaRuntime.getDefaultVMInstall() kicks in and we get the 24 VM as the implementation for JavaSE-17. This doesn't work for the test at hand. Actually it's the exact case where #756 would raise an alarm 😄 😄.

Ergo: the idea of using ranges for searching necessary VMs, nice as it sounds, is not appropriate. We do need exact matches, when launches use execution environments.

@laeubi
Copy link
Copy Markdown
Contributor Author

laeubi commented Aug 7, 2025

These are the versions picked: 15, 19, 24. Interesting choice, but no error, right?

Yes this sounds valid.

When launching a VM for JavaSE-17, JREContainerInitializer.resolveVM(IExecutionEnvironment) does not find a match that isStrictlyCompatible(). In this case JavaRuntime.getDefaultVMInstall() kicks in and we get the 24 VM as the implementation for JavaSE-17

This does sounds wrong and is not what one would expect. If no strictly compatible one is found the next "better" should be chosen instead.

I might be able to mitigate this in the test, but in general this does not sound like what a user wants, this is also what I see in the preferences here:

grafik

so as a user I would expect that JDT picks jdk-17 as the default (even though it does not make any claims how multiple ones are ordered) for a a EE 12.

@laeubi
Copy link
Copy Markdown
Contributor Author

laeubi commented Aug 7, 2025

I slightly tweaked the test and can reproduce the issue now, will take a look into it. In the end one could of course require a strict JVM but this seems to limited given that the given range is all valid for the MR use-case... so I think that needs to be fixed or at least better understood.

@laeubi
Copy link
Copy Markdown
Contributor Author

laeubi commented Aug 8, 2025

I have now created

and added a commit here to fix the selection of JVM, if that works I would extract this into an own PR as it does not really belongs to this change.

@stephan-herrmann
Copy link
Copy Markdown
Contributor

I have now created

and added a commit here to fix the selection of JVM, if that works I would extract this into an own PR as it does not really belongs to this change.

So, #760 should be fixed first, to ensure that tests introduced here work reliably, right?

@laeubi
Copy link
Copy Markdown
Contributor Author

laeubi commented Aug 9, 2025

So, #760 should be fixed first, to ensure that tests introduced here work reliably, right?

Yes I'll slit up a dedicated PR fro this now. But its not only for the test but in general one would expect the best matching VM to be selected for a EE.

@stephan-herrmann
Copy link
Copy Markdown
Contributor

But its not only for the test but in general

I was only discussing in which order PRs should be merged.

@laeubi
Copy link
Copy Markdown
Contributor Author

laeubi commented Aug 9, 2025

But its not only for the test but in general

I was only discussing in which order PRs should be merged.

PR is here:

if that is merged, I would rebase /squash this PR and then it should be ready for merging as well or at least for a next round of review.

@laeubi
Copy link
Copy Markdown
Contributor Author

laeubi commented Sep 16, 2025

@stephan-herrmann as the other PR is merged now and new stream started I rebased the PR, from my side it would be ready to be merged to be included in M1!

@eclipse-jdt-bot
Copy link
Copy Markdown
Contributor

This pull request changes some projects for the first time in this development cycle.
Therefore the following files need a version increment:

org.eclipse.jdt.debug.tests/META-INF/MANIFEST.MF
org.eclipse.jdt.debug.tests/pom.xml
org.eclipse.jdt.debug.ui/META-INF/MANIFEST.MF
org.eclipse.jdt.debug.ui/pom.xml

An additional commit containing all the necessary changes was pushed to the top of this PR's branch. To obtain these changes (for example if you want to push more changes) either fetch from your fork or apply the git patch.

Git patch
From 20b3d10b7d39359a3d9fb2f3efb106411d38f5c7 Mon Sep 17 00:00:00 2001
From: Eclipse JDT Bot <jdt-bot@eclipse.org>
Date: Tue, 16 Sep 2025 06:46:10 +0000
Subject: [PATCH] Version bump(s) for 4.38 stream


diff --git a/org.eclipse.jdt.debug.tests/META-INF/MANIFEST.MF b/org.eclipse.jdt.debug.tests/META-INF/MANIFEST.MF
index 3da93ba22..55a4021ec 100644
--- a/org.eclipse.jdt.debug.tests/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.debug.tests/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.jdt.debug.tests; singleton:=true
-Bundle-Version: 3.12.750.qualifier
+Bundle-Version: 3.12.850.qualifier
 Bundle-ClassPath: javadebugtests.jar
 Bundle-Activator: org.eclipse.jdt.debug.testplugin.JavaTestPlugin
 Bundle-Vendor: %providerName
diff --git a/org.eclipse.jdt.debug.tests/pom.xml b/org.eclipse.jdt.debug.tests/pom.xml
index d59fbe711..e8df0f8a9 100644
--- a/org.eclipse.jdt.debug.tests/pom.xml
+++ b/org.eclipse.jdt.debug.tests/pom.xml
@@ -18,7 +18,7 @@
   </parent>
   <groupId>org.eclipse.jdt</groupId>
   <artifactId>org.eclipse.jdt.debug.tests</artifactId>
-  <version>3.12.750-SNAPSHOT</version>
+  <version>3.12.850-SNAPSHOT</version>
   <packaging>eclipse-test-plugin</packaging>
   <properties>
     <testSuite>${project.artifactId}</testSuite>
diff --git a/org.eclipse.jdt.debug.ui/META-INF/MANIFEST.MF b/org.eclipse.jdt.debug.ui/META-INF/MANIFEST.MF
index a4f478043..58c8e615c 100644
--- a/org.eclipse.jdt.debug.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.debug.ui/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.jdt.debug.ui; singleton:=true
-Bundle-Version: 3.15.100.qualifier
+Bundle-Version: 3.15.200.qualifier
 Bundle-Activator: org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
diff --git a/org.eclipse.jdt.debug.ui/pom.xml b/org.eclipse.jdt.debug.ui/pom.xml
index 212983904..dcfe6c67b 100644
--- a/org.eclipse.jdt.debug.ui/pom.xml
+++ b/org.eclipse.jdt.debug.ui/pom.xml
@@ -18,7 +18,7 @@
   </parent>
   <groupId>org.eclipse.jdt</groupId>
   <artifactId>org.eclipse.jdt.debug.ui</artifactId>
-  <version>3.15.100-SNAPSHOT</version>
+  <version>3.15.200-SNAPSHOT</version>
   <packaging>eclipse-plugin</packaging>
   <properties>
   	<defaultSigning-excludeInnerJars>true</defaultSigning-excludeInnerJars>
-- 
2.51.0

Further information are available in Common Build Issues - Missing version increments.

Currently when one launches a multi-release compiled project one always
gets the type from the default project folder.

This now first checks what JRE is used to launch, then adds all valid
folders in reverse version order to the classpath to emulate the
behavior of loading a multi-release jar at runtime.

Fix https://github.com/eclipse-jdt/eclipse.jdt.core/issues/4276
@laeubi
Copy link
Copy Markdown
Contributor Author

laeubi commented Sep 25, 2025

Rebased and conflicst resolved

@laeubi
Copy link
Copy Markdown
Contributor Author

laeubi commented Sep 25, 2025

@stephan-herrmann anything needed here to make this merged?

@stephan-herrmann stephan-herrmann self-requested a review September 27, 2025 11:42
Copy link
Copy Markdown
Contributor

@stephan-herrmann stephan-herrmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dropped some more observations into eclipse-jdt/eclipse.jdt.core#4275 (comment) which signal that we are not "done".

Still the current PR is a good step forward.

@stephan-herrmann stephan-herrmann merged commit 193c879 into eclipse-jdt:master Sep 27, 2025
13 checks passed
@iloveeclipse
Copy link
Copy Markdown
Member

Caused regression in SDK tests: #786

@laeubi
Copy link
Copy Markdown
Contributor Author

laeubi commented Sep 29, 2025

Caused regression in SDK tests: #786

Just wondering could it be a regression if the test never was there before? ;-)

@iloveeclipse
Copy link
Copy Markdown
Member

Rgression in the sense that the tests are failing now and they were not failing before (independently because they were created, added, removed, modified or whatesever).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MR launching multi-release-compiled projects

4 participants